﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Input;

namespace LunarSF.SHomeWorkshop.LunarMarkdownEditor
{
    public class TextCommandManager : List<TextCommandItem>
    {
        private static TextCommandManager cmds;
        /// <summary>
        /// 整个程序应处理的文本命令都应在此类中注册。
        /// </summary>
        public static TextCommandManager TextCommands
        {
            get { return cmds; }
        }

        public static string inputedCommandText = "";
        /// <summary>
        /// 指用户连续输入的字符构成的命令。一条命令可能通过多次输入实现。
        /// </summary>
        public static string InputedCommandText
        {
            get { return inputedCommandText; }
            set
            {
                inputedCommandText = value;
                Globals.MainWindow.tbTextCommand.Text = value;
            }
        }

        public static string TryToRun(string previewcmdText, string cmdText, out List<TextCommandItem> matchedTextCommandItems)
        {
            matchedTextCommandItems = new List<TextCommandItem>();

            var items = GetMatchedTextCommands(previewcmdText + cmdText);
            if (items.Count == 1)
            {
                matchedTextCommandItems.Add(items[0]);
                var result = items[0].Run(items[0]);
                if (result)
                {
                    InputedCommandText = "";
                    return "完成";//这个不能修改
                }
                else
                {
                    //inputedCommandText不变
                    return "未能执行命令。";
                }
            }

            if (items.Count < 1)
            {
                return "无此命令。";
            }

            matchedTextCommandItems.AddRange(items);
            InputedCommandText += cmdText;
            return "等待继续输入...";                      //有多个符合条件的命令。
        }

        public static List<TextCommandItem> GetMatchedTextCommands(string cmdText)
        {
            List<TextCommandItem> items = new List<TextCommandItem>();

            foreach (var item in cmds)
            {
                switch (item.Type)
                {
                    case TextCommandType.NormalText:  //纯文本命令
                        {
                            if (item.CommandText.StartsWith(cmdText))
                                items.Add(item);
                            break;
                        }
                    case TextCommandType.RegexText:   //使用正则表达式的命令
                        {
                            Regex regex = new Regex(item.CommandText);
                            var match = regex.Match(cmdText);
                            if (match.Success) items.Add(item);
                            break;
                        }
                }
            }

            return items;
        }

        static TextCommandManager()
        {
            cmds = new TextCommandManager();

            cmds.Add(new TextCommandItem("i", TextCommandType.NormalText, "在当前字符前面插入字符。") { Run = InsertBefore, });
            cmds.Add(new TextCommandItem("a", TextCommandType.NormalText, "在当前字符后面插入字符。") { Run = InsertAfter, });

            cmds.Add(new TextCommandItem("h", TextCommandType.NormalText, "向左移动光标。") { Run = MoveLeft, });
            cmds.Add(new TextCommandItem("j", TextCommandType.NormalText, "向下移动光标。") { Run = MoveDown, });
            cmds.Add(new TextCommandItem("k", TextCommandType.NormalText, "向上移动光标。") { Run = MoveUp, });
            cmds.Add(new TextCommandItem("l", TextCommandType.NormalText, "向右移动光标。") { Run = MoveRight, });
            //高频快捷键不能安排双字符或更多字符的按键。例如：不应安排la lb 这样的快捷键组合，因为会造成 l 键须等待进一步选择。

            cmds.Add(new TextCommandItem("w", TextCommandType.NormalText, "移动光标到下一个词。") { Run = MoveNextWord, });
            cmds.Add(new TextCommandItem("b", TextCommandType.NormalText, "移动光标到上一个词。") { Run = MovePreviewWord, });

            cmds.Add(new TextCommandItem("d", TextCommandType.NormalText, "剪切。") { Run = Cut, });

            cmds.Add(new TextCommandItem("y", TextCommandType.NormalText, "复制。") { Run = Copy, });
            cmds.Add(new TextCommandItem("p", TextCommandType.NormalText, "粘贴。") { Run = Paste, });
            cmds.Add(new TextCommandItem("pp", TextCommandType.NormalText, "粘贴。") { Run = Paste, });
            //单独输入一个p要等等或按回车，若直接输入两个p应该是极好的。

            cmds.Add(new TextCommandItem("u", TextCommandType.NormalText, "撤销。") { Run = Undo, });

            cmds.Add(new TextCommandItem("x", TextCommandType.NormalText, "删除当前字符。") { Run = Delete, });

            cmds.Add(new TextCommandItem("o", TextCommandType.NormalText, "新插入一行，并在新行头部进入编辑模式。") { Run = InsertNewLine, });

            //注意：所有的大写母快捷键组合都必须在小写字母之下，因为涉及顺序。

            cmds.Add(new TextCommandItem("A", TextCommandType.NormalText, "在行尾插入。") { Run =AppandAtLineEnd, });
            cmds.Add(new TextCommandItem("I", TextCommandType.NormalText, "在行首插入。") { Run = AppandAtLineHeader, });

            //合并行
            cmds.Add(new TextCommandItem("J", TextCommandType.NormalText, "合并行。") { Run = JoinLines, });
            cmds.Add(new TextCommandItem("O", TextCommandType.NormalText, "在当前行上侧新插入一行，并在新行头部进入编辑模式。") { Run = InsertNewLineAtTop, });

            //注意：所有的双字母快捷键组合都必须在单字母之下，因为涉及顺序。

            cmds.Add(new TextCommandItem("dd", TextCommandType.NormalText, "删除当前行。") { Run = DeleteLine, });

            cmds.Add(new TextCommandItem("rh", TextCommandType.NormalText, "移动到当前行行首。") { Run = MoveToLineStart, });
            cmds.Add(new TextCommandItem("re", TextCommandType.NormalText, "移动到当前行行尾。") { Run = MoveToLineEnd, });

            cmds.Add(new TextCommandItem("pf", TextCommandType.NormalText, "向下翻页。") { Run = PageDown, });
            cmds.Add(new TextCommandItem("pb", TextCommandType.NormalText, "向上翻页。") { Run = PageUp, });

            cmds.Add(new TextCommandItem("pu", TextCommandType.NormalText, "向上翻半页。") { Run = PageUpHalf, });
            cmds.Add(new TextCommandItem("pd", TextCommandType.NormalText, "向下翻半页。") { Run = PageDownHalf, });

            //添加[Step]标签

            cmds.Add(new TextCommandItem("ss", TextCommandType.NormalText, "在行首添加 [Step] 标记") { Run = AddStepMark, });
            cmds.Add(new TextCommandItem("sb", TextCommandType.NormalText, "在行首添加 [Step Begin] 标记") { Run = AddStepStartMark, });
            cmds.Add(new TextCommandItem("se", TextCommandType.NormalText, "在行首添加 [Step End] 标记") { Run = AddStepEndMark, });
        }


        /// <summary>
        /// 在当前行首进入编辑模式。
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        static bool AppandAtLineHeader(TextCommandItem item)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;

            ae.EditingMode = EditingMode.NormalMode;

            ae.EditingMode = EditingMode.NormalMode;
            var line = ae.EditorBase.Document.GetLineByOffset(ae.EditorBase.SelectionStart);
            ae.EditorBase.Select(line.Offset, 0);

            if (ae.PreviewInputMethodState.HasValue)
            {
                Globals.SwitchInputMethod(true);
                InputMethod.Current.ImeState = ae.PreviewInputMethodState.Value;
            }
            return true;
        }

        /// <summary>
        /// 在当前行尾进入编辑模式。
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        static bool AppandAtLineEnd(TextCommandItem item)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;

            ae.EditingMode = EditingMode.NormalMode;
            var line = ae.EditorBase.Document.GetLineByOffset(ae.EditorBase.SelectionStart+ae.EditorBase.SelectionLength);
            ae.EditorBase.Select(line.EndOffset, 0);

            if (ae.PreviewInputMethodState.HasValue)
            {
                Globals.SwitchInputMethod(true);
                InputMethod.Current.ImeState = ae.PreviewInputMethodState.Value;
            }
            return true;
        }

        public static bool AddStepStartMark(TextCommandItem item)
        {
            return AddStepMark("[Step Begin] ");
        }

        public static bool AddStepEndMark(TextCommandItem item)
        {
            return AddStepMark("[Step End] ");
        }

        public static bool AddStepMark(TextCommandItem item)
        {
            return AddStepMark("[Step] ");
        }

        private static bool AddStepMark(string stepText)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;

            var fstLine = ae.EditorBase.Document.GetLineByOffset(ae.EditorBase.SelectionStart);
            var lstLine = ae.EditorBase.Document.GetLineByOffset(ae.EditorBase.SelectionStart + ae.EditorBase.SelectionLength);

            Regex regexStepLine = new Regex(@"^[ 　]{0,3}\[[ 　]{0,}[sSｓＳ]([tTｔＴ][eEｅＥ][pPｐＰ]){0,1}[ 　]{0,}(([bBｂＢ]([eEｅＥ][gGｇＧ][iIｉＩ][nNｎＮ]){0,1})|([eEｅＥ]([nNｎＮ][dDｄＤ]){0,1})){0,1}\][ 　]{0,}");

            ae.EditorBase.BeginChange();

            for (int i = fstLine.LineNumber; i <= lstLine.LineNumber; i++)
            {
                var line = ae.EditorBase.Document.GetLineByNumber(i);
                if (line == null) continue;
                var lineText = ae.EditorBase.Document.GetText(line.Offset, line.Length);
                var match = regexStepLine.Match(lineText);
                if (match.Success)
                {
                    ae.EditorBase.Document.Replace(line.Offset, line.Length, stepText + lineText.Substring(match.Length));
                }
                else
                {
                    ae.EditorBase.Document.Replace(line.Offset, line.Length, stepText + lineText);
                }
            }

            ae.EditorBase.EndChange();

            return true;
        }

        public static bool JoinLines(TextCommandItem item)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;

            var e = ae.EditorBase;
            var fstLine = e.Document.GetLineByOffset(e.SelectionStart);
            var lstLine = e.Document.GetLineByOffset(e.SelectionStart + e.SelectionLength);

            if (fstLine.LineNumber == lstLine.LineNumber)
            {
                e.Document.Remove(e.Document.GetLineByOffset(e.SelectionStart).EndOffset, 2);
                return true;
            }
            else if (fstLine.LineNumber < lstLine.LineNumber)
            {
                e.BeginChange();

                var selStartOffset = e.SelectionStart;
                var selLength = e.SelectionLength;
                var srcText = e.SelectedText;
                srcText = srcText.Replace("\r", "").Replace("\n", "");
                e.Document.Replace(selStartOffset, selLength, srcText);
                e.EndChange();
                return true;
            }

            return true;
        }

        public static bool Cut(TextCommandItem item)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;

            ae.EditorBase.Cut();

            return true;
        }

        public static bool Copy(TextCommandItem item)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;

            ae.EditorBase.Copy();

            return true;
        }

        public static bool Paste(TextCommandItem item)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;

            ae.EditorBase.BeginChange();

            ae.EditorBase.Document.Replace(ae.EditorBase.SelectionStart, ae.EditorBase.SelectionLength, Clipboard.GetText(TextDataFormat.UnicodeText));

            ae.EditorBase.EndChange();

            return true;
        }

        public static bool Delete(TextCommandItem item)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;

            ae.EditorBase.Document.Remove(ae.EditorBase.SelectionStart, ae.EditorBase.SelectionLength);

            return true;
        }

        public static bool DeleteLine(TextCommandItem item)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;

            var fstLine = ae.EditorBase.Document.GetLineByOffset(ae.EditorBase.SelectionStart);
            var endLine = ae.EditorBase.Document.GetLineByOffset(ae.EditorBase.SelectionStart + ae.EditorBase.SelectionLength);

            ae.EditorBase.BeginChange();
            for (int i = endLine.LineNumber; i >= fstLine.LineNumber; i--)
            {
                ae.EditorBase.Document.Remove(ae.EditorBase.Document.GetLineByNumber(i));
            }
            ae.EditorBase.EndChange();
            return true;
        }

        public static bool Undo(TextCommandItem item)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;

            if (ae.EditorBase.CanRedo) ae.EditorBase.Undo();

            return true;
        }

        /// <summary>
        /// 在当前行的上侧另起一个新行，并在新行头部进入编辑模式。
        /// </summary>
        /// <returns></returns>
        public static bool InsertNewLineAtTop(TextCommandItem item)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;

            var lineOffset = ae.EditorBase.Document.GetLineByOffset(ae.EditorBase.SelectionStart).Offset;
            ae.EditorBase.BeginChange();

            ae.EditorBase.Document.Insert(lineOffset, "\r\n");
            ae.EditorBase.EndChange();

            ae.EditorBase.Select(lineOffset, 0);
            ae.EditingMode = EditingMode.NormalMode;
            if (ae.PreviewInputMethodState.HasValue)
            {
                Globals.SwitchInputMethod(true);
                InputMethod.Current.ImeState = ae.PreviewInputMethodState.Value;
            }

            return true;
        }

        /// <summary>
        /// 在当前行下面另起一行，并在新行首进入编辑模式。
        /// </summary>
        /// <returns></returns>
        public static bool InsertNewLine(TextCommandItem item)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;

            var lineEndOffset = ae.EditorBase.Document.GetLineByOffset(ae.EditorBase.SelectionStart).EndOffset;
            ae.EditorBase.BeginChange();

            ae.EditorBase.Document.Insert(lineEndOffset, "\r\n");
            ae.EditorBase.EndChange();

            ae.EditorBase.Select(lineEndOffset + 2, 0);
            ae.EditingMode = EditingMode.NormalMode;
            if (ae.PreviewInputMethodState.HasValue)
            {
                Globals.SwitchInputMethod(true);
                InputMethod.Current.ImeState = ae.PreviewInputMethodState.Value;
            }
            return true;
        }

        /// <summary>
        /// 在当前字符后面进入编辑模式。
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        static bool InsertAfter(TextCommandItem item)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;

            ae.EditorBase.Select(ae.EditorBase.SelectionStart + 1, 0);
            ae.EditingMode = EditingMode.NormalMode;
            if (ae.PreviewInputMethodState.HasValue)
            {
                Globals.SwitchInputMethod(true);
                InputMethod.Current.ImeState = ae.PreviewInputMethodState.Value;
            }
            return true;
        }

        /// <summary>
        /// 在当前字符前面进入编辑模式。
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        static bool InsertBefore(TextCommandItem item)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;

            ae.EditingMode = EditingMode.NormalMode;
            if (ae.PreviewInputMethodState.HasValue)
            {
                Globals.SwitchInputMethod(true);
                InputMethod.Current.ImeState = ae.PreviewInputMethodState.Value;
            }
            return true;
        }

        /// <summary>
        /// 向左移动光标。
        /// </summary>
        static bool MoveLeft(TextCommandItem item)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;
            if (ae.EditorBase.SelectionLength > 0)
            {
                var selStartOffset = ae.EditorBase.SelectionStart;
                ae.EditorBase.Select(selStartOffset, 0);
            }
            ae.EditorBase.MoveCaret(ICSharpCode.AvalonEdit.Editing.CaretMovementType.CharLeft);
            return true;
        }

        /// <summary>
        /// 向右移动光标。
        /// </summary>
        static bool MoveRight(TextCommandItem item)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;

            var selStartOffset = ae.EditorBase.SelectionStart;
            if (ae.EditorBase.SelectionLength > 0)
            {
                ae.EditorBase.Select(selStartOffset, 0);
            }

            //要注意不能选中最后一个位置
            if (selStartOffset < ae.EditorBase.Document.TextLength - 1)
            {
                ae.EditorBase.MoveCaret(ICSharpCode.AvalonEdit.Editing.CaretMovementType.CharRight);
            }

            return true;
        }

        /// <summary>
        /// 向上移动光标。
        /// </summary>
        static bool MoveUp(TextCommandItem item)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;

            if (ae.EditorBase.SelectionLength > 0)
            {
                var selStartOffset = ae.EditorBase.SelectionStart;
                ae.EditorBase.Select(selStartOffset, 0);
            }
            ae.EditorBase.MoveCaret(ICSharpCode.AvalonEdit.Editing.CaretMovementType.LineUp);
            return true;
        }

        /// <summary>
        /// 向下移动光标。
        /// </summary>
        static bool MoveDown(TextCommandItem item)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;

            if (ae.EditorBase.SelectionLength > 0)
            {
                var selStartOffset = ae.EditorBase.SelectionStart;
                ae.EditorBase.Select(selStartOffset, 0);
            }
            ae.EditorBase.MoveCaret(ICSharpCode.AvalonEdit.Editing.CaretMovementType.LineDown);
            return true;
        }

        /// <summary>
        /// 光标移动到行首。
        /// </summary>
        static bool MoveToLineStart(TextCommandItem item)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;

            if (ae.EditorBase.SelectionLength > 0)
            {
                var selStartOffset = ae.EditorBase.SelectionStart;
                ae.EditorBase.Select(selStartOffset, 0);
            }
            ae.EditorBase.MoveCaret(ICSharpCode.AvalonEdit.Editing.CaretMovementType.LineStart);
            return true;
        }

        /// <summary>
        /// 光标移动到行尾。
        /// </summary>
        static bool MoveToLineEnd(TextCommandItem item)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;

            if (ae.EditorBase.SelectionLength > 0)
            {
                var selStartOffset = ae.EditorBase.SelectionStart;
                ae.EditorBase.Select(selStartOffset, 0);
            }
            ae.EditorBase.MoveCaret(ICSharpCode.AvalonEdit.Editing.CaretMovementType.LineEnd);
            return true;
        }

        /// <summary>
        /// 光标移动到下一个词。
        /// </summary>
        static bool MoveNextWord(TextCommandItem item)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;

            if (ae.EditorBase.SelectionLength > 0)
            {
                var selStartOffset = ae.EditorBase.SelectionStart;
                ae.EditorBase.Select(selStartOffset, 0);
            }
            ae.EditorBase.MoveCaret(ICSharpCode.AvalonEdit.Editing.CaretMovementType.WordRight);
            return true;
        }

        /// <summary>
        /// 光标移动到上一个词。
        /// </summary>
        static bool MovePreviewWord(TextCommandItem item)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;

            if (ae.EditorBase.SelectionLength > 0)
            {
                var selStartOffset = ae.EditorBase.SelectionStart;
                ae.EditorBase.Select(selStartOffset, 0);
            }
            ae.EditorBase.MoveCaret(ICSharpCode.AvalonEdit.Editing.CaretMovementType.WordLeft);
            return true;
        }

        /// <summary>
        /// 向下翻页。
        /// </summary>
        static bool PageDown(TextCommandItem item)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;

            if (ae.EditorBase.SelectionLength > 0)
            {
                var selStartOffset = ae.EditorBase.SelectionStart;
                ae.EditorBase.Select(selStartOffset, 0);
            }
            ae.EditorBase.MoveCaret(ICSharpCode.AvalonEdit.Editing.CaretMovementType.PageDown);
            return true;
        }

        /// <summary>
        /// 向上翻页。
        /// </summary>
        static bool PageUp(TextCommandItem item)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;

            if (ae.EditorBase.SelectionLength > 0)
            {
                var selStartOffset = ae.EditorBase.SelectionStart;
                ae.EditorBase.Select(selStartOffset, 0);
            }
            ae.EditorBase.MoveCaret(ICSharpCode.AvalonEdit.Editing.CaretMovementType.PageUp);
            return true;
        }

        /// <summary>
        /// 向上翻半页。
        /// </summary>
        static bool PageUpHalf(TextCommandItem item)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;

            if (ae.EditorBase.SelectionLength > 0)
            {
                var selStartOffset = ae.EditorBase.SelectionStart;
                ae.EditorBase.Select(selStartOffset, 0);
            }
            ae.EditorBase.MoveCaret(ICSharpCode.AvalonEdit.Editing.CaretMovementType.PageUpHalf);
            return true;
        }

        /// <summary>
        /// 向下翻半页。
        /// </summary>
        static bool PageDownHalf(TextCommandItem item)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null) return false;

            if (ae.EditorBase.SelectionLength > 0)
            {
                var selStartOffset = ae.EditorBase.SelectionStart;
                ae.EditorBase.Select(selStartOffset, 0);
            }
            ae.EditorBase.MoveCaret(ICSharpCode.AvalonEdit.Editing.CaretMovementType.PageDownHalf);
            return true;
        }
    }
}
